import os
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.stats.diagnostic import acorr_ljungbox
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from mlxtend.frequent_patterns import apriori
from sklearn.preprocessing import LabelEncoder
from sklearn.metrics import mean_squared_error
import warnings
# 修改：替换psycopg2为pyodbc
import pyodbc
from sqlalchemy import create_engine
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus'] = False


# ---------------------- 定义目标保存路径 ----------------------
TARGET_SAVE_PATH = "生产线趋势图"
# 自动创建路径（若不存在）
if not os.path.exists(TARGET_SAVE_PATH):
    os.makedirs(TARGET_SAVE_PATH)
    print(f"已创建目标保存目录：{TARGET_SAVE_PATH}")
else:
    print(f"目标保存目录已存在：{TARGET_SAVE_PATH}")


# 1. 数据库配置与连接 - 修改为ODBC配置
DB_CONFIG = {
    "driver": "OpenGauss",
    "host": "localhost",
    "user": "omm",
    "password": "Mind123#",  # 修正密码
    "database": "postgres",
    "port": "5432"
}

# 创建SQLAlchemy引擎 - 修改为ODBC连接
try:
    engine = create_engine(
        f"postgresql+pyodbc://{DB_CONFIG['user']}:{DB_CONFIG['password']}@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}?driver={DB_CONFIG['driver'].replace(' ', '+')}"
    )
except Exception as e:
    print(f"ODBC SQLAlchemy引擎创建失败: {e}")
    # 备用方案：使用psycopg2
    try:
        import psycopg2
        engine = create_engine(
            f"postgresql+psycopg2://{DB_CONFIG['user']}:{DB_CONFIG['password']}@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}"
        )
        print("使用备用psycopg2连接")
    except Exception as e2:
        print(f"备用连接也失败: {e2}")
        engine = None

columns_to_keep = [
    "日期","时间","生产线编号","物料推送数","填装数","加盖数","拧盖数","合格数","不合格数",
    "物料推送装置故障1001","物料检测装置故障2001","填装装置检测故障4001","填装装置定位故障4002",
    "填装装置填装故障4003","加盖装置定位故障5001","加盖装置加盖故障5002","拧盖装置定位故障6001","拧盖装置拧盖故障6002"
]

# 从openGauss读取数据
dataframes = []
# 修改表名映射
table_mapping = {
    'M301': 'fujian_3.m301',
    'M302': 'fujian_3.m302', 
    'M303': 'fujian_3.m303',
    'M304': 'fujian_3.m304',
    'M305': 'fujian_3.m305',
    'M306': 'fujian_3.m306',
    'M307': 'fujian_3.m307',
    'M308': 'fujian_3.m308',
    'M309': 'fujian_3.m309',
    'M310': 'fujian_3.m310'
}

# 直接使用ODBC连接读取数据（如果SQLAlchemy失败）
def read_data_with_odbc(table_name, columns):
    """使用ODBC直接读取数据"""
    try:
        conn_str = f"DRIVER={DB_CONFIG['driver']};SERVER={DB_CONFIG['host']};PORT={DB_CONFIG['port']};DATABASE={DB_CONFIG['database']};UID={DB_CONFIG['user']};PWD={DB_CONFIG['password']}"
        conn = pyodbc.connect(conn_str)
        
        # 构建查询 - 修正引号格式
        columns_str = ', '.join([f'"{col}"' for col in columns])
        query = f'SELECT {columns_str} FROM {table_name}'
        
        df = pd.read_sql(query, conn)
        conn.close()
        return df
    except Exception as e:
        print(f"ODBC直接读取失败: {e}")
        return None

for line_id, table_name in table_mapping.items():
    try:
        # 直接使用ODBC读取，跳过SQLAlchemy部分
        df = read_data_with_odbc(table_name, columns_to_keep)
        if df is None:
            raise Exception("ODBC直接读取失败")
        
        df["生产线编号"] = line_id  # 直接使用映射的产线编号
        dataframes.append(df)
        print(f"成功读取openGauss表：{table_name}（对应生产线编号：{line_id}），数据行数：{len(df)}")
    except Exception as e:
        print(f"读取表{table_name}失败：{str(e)}")

if len(dataframes) != 10:
    raise ValueError("未能读取全部10张表，请检查表名或数据库连接")


# 2. 数据预处理与单生产线图表
list_target_dfs = []
for df in dataframes:
    # 缺失值处理（不变）
    if df.isnull().values.any():
        print(f"DataFrame（{df['生产线编号'].iloc[0]}）中存在缺失值，已前向填充")
        df.fillna(method='ffill', inplace=True)
    else:
        print(f"DataFrame（{df['生产线编号'].iloc[0]}）中不存在缺失值")

    # 故障字段二进制转换（不变）
    binary_fault_columns = [
        '物料推送装置故障1001', '物料检测装置故障2001',
        '填装装置检测故障4001', '填装装置定位故障4002',
        '填装装置填装故障4003', '加盖装置定位故障5001',
        '加盖装置加盖故障5002', '拧盖装置定位故障6001',
        '拧盖装置拧盖故障6002'
    ]
    for col in binary_fault_columns:
        df[col] = df[col].apply(lambda x: 1 if x else 0)

    # 日期时间处理（不变）
    df['日期'] = df['日期'].astype(int)
    df['时间'] = df['时间'].astype(int)
    df['时间戳'] = (df['日期'] - 1) * 86400 + df['时间']
    df['生产线编号'] = df['生产线编号'].astype('category')
    df['日期时间'] = df['日期'] + df['时间']

    # 提取每日最后一秒数据（不变）
    daily_last_second = df.groupby('日期')['时间'].max().reset_index()
    df = pd.merge(daily_last_second, df, on=['日期', '时间'], how='inner')

    # 计算核心指标（不变）
    df['产量'] = df['物料推送数'] + df['填装数'] + df['加盖数'] + df['拧盖数']
    df['合格率'] = df['合格数'] / (df['合格数'] + df['不合格数'])
    df['合格差额'] = df['合格数'] - df['不合格数']

    # ---------------------- 修改1：单生产线图表保存到目标路径 ----------------------
    line_id = df['生产线编号'].iloc[0]

    # 3.1 单生产线产量随时间变化图
    plt.figure(figsize=(12, 6))
    plt.plot(df['日期'], df['产量'], marker='o', linestyle='-')
    plt.title(f'{line_id} 产量随时间变化')  # 标题添加生产线编号，避免混淆
    plt.xlabel('日期')
    plt.ylabel('产量')
    plt.grid(True)
    plt.xticks(rotation=45)
    plt.tight_layout()
    # 保存路径改为目标路径，文件名包含生产线编号（更清晰）
    production_filename = f"{TARGET_SAVE_PATH}/{line_id}_产量随时间变化.png"
    plt.savefig(production_filename, dpi=100)
    print(f'单生产线产量图保存为：{production_filename}')
    plt.close()  # 关闭画布，避免内存占用

    # 3.2 单生产线合格率随时间变化图
    plt.figure(figsize=(12, 6))
    plt.bar(df['日期'], df['合格率'])
    plt.title(f'{line_id} 合格率随时间变化')  # 标题添加生产线编号
    plt.xlabel('日期')
    plt.ylabel('合格率')
    plt.grid(True)
    plt.xticks(rotation=45)
    plt.ylim(0.99, 1.0)
    plt.tight_layout()
    # 保存路径改为目标路径，文件名包含生产线编号
    qualification_filename = f"{TARGET_SAVE_PATH}/{line_id}_合格率随时间变化.png"
    plt.savefig(qualification_filename, dpi=100)
    print(f"单生产线合格率图保存为：{qualification_filename}")
    plt.close()

    list_target_dfs.append(df)


# 3. 多生产线汇总图表（修改保存路径）
daily_last_second_data = pd.concat(list_target_dfs, ignore_index=True)

# 3.1 各生产线平均指标计算与图表
averages = daily_last_second_data.groupby('生产线编号')[['产量', '合格率', '合格数']].mean().rename(columns={
    '产量': '平均产量',
    '合格率': '平均合格率',
    '合格数': '平均合格数'
})
print("\n各生产线平均指标：")
print(averages)

# ---------------------- 修改2：平均指标对比图保存到目标路径 ----------------------
fig, axes = plt.subplots(nrows=1, ncols=3, figsize=(14, 6))
# 子图1：平均产量
axes[0].bar(averages.index, averages['平均产量'], width=0.4, label='平均产量', color='steelblue', align='center')
axes[0].set_title('生产线的平均产量')
axes[0].set_xlabel('生产线编号')
axes[0].set_ylabel('平均产量')
axes[0].legend(title='指标')
axes[0].set_ylim(bottom=11000, top=12000)
# 子图2：平均合格率
axes[1].bar(averages.index, averages['平均合格率'], width=0.4, label='平均合格率', color='pink', align='center')
axes[1].set_title('生产线的平均合格率')
axes[1].set_xlabel('生产线编号')
axes[1].set_ylabel('平均合格率')
axes[1].legend(title='指标')
axes[1].set_ylim(bottom=0.997, top=1.0)
# 子图3：平均合格数
axes[2].bar(averages.index, averages['平均合格数'], width=0.4, label='平均合格数', color='steelblue', align='center')
axes[2].set_title('生产线的平均合格数')
axes[2].set_xlabel('生产线编号')
axes[2].set_ylabel('平均合格数')
axes[2].legend(title='指标')
axes[2].set_ylim(bottom=1300)
plt.tight_layout()
# 保存路径改为目标路径
avg_indicators_filename = f"{TARGET_SAVE_PATH}/各生产线平均指标对比图.png"
plt.savefig(avg_indicators_filename, dpi=100)
print(f'平均指标对比图保存为：{avg_indicators_filename}')
plt.close()

# 3.2 各生产线统计量（最大/最小/总产量）计算与图表
averages = daily_last_second_data.groupby('生产线编号')[['产量', '合格率']].mean().rename(columns={
    '产量': '平均产量', '合格率': '平均合格率'
})
max_values = daily_last_second_data.groupby('生产线编号')[['产量', '合格率']].max().rename(columns={
    '产量': '最大产量', '合格率': '最大合格率'
})
min_values = daily_last_second_data.groupby('生产线编号')[['产量', '合格率']].min().rename(columns={
    '产量': '最小产量', '合格率': '最小合格率'
})
total_production = daily_last_second_data.groupby('生产线编号')['产量'].sum().rename('年度总产量')
stats_per_line = pd.concat([averages, max_values, min_values, total_production], axis=1)
print("\n各生产线完整统计量：")
print(stats_per_line)

# ---------------------- 修改3：产量统计对比图保存到目标路径 ----------------------
production_stats_columns = ['最大产量', '最小产量', '年度总产量']
fig1, axes1 = plt.subplots(nrows=1, ncols=3, figsize=(18, 6))
for i, column in enumerate(production_stats_columns):
    bars = axes1[i].bar(stats_per_line.index, stats_per_line[column], color='skyblue')
    axes1[i].set_title(f'{column}')
    axes1[i].set_xlabel('生产线编号')
    axes1[i].set_ylabel('统计量')
    axes1[i].set_ylim(bottom=10000)
    # 添加数值标签
    for bar in bars:
        height = bar.get_height()
        axes1[i].annotate(f'{height}',
                          xy=(bar.get_x() + bar.get_width() / 2, height),
                          xytext=(0, 3),
                          textcoords="offset points",
                          ha='center', va='bottom')
plt.tight_layout()
# 保存路径改为目标路径
production_stats_filename = f"{TARGET_SAVE_PATH}/各生产线产量统计对比图.png"
plt.savefig(production_stats_filename, dpi=100)
print(f'产量统计对比图保存为：{production_stats_filename}')
plt.close()

# ---------------------- 修改4：合格率统计对比图保存到目标路径 ----------------------
qualification_stats_columns = ['最大合格率', '最小合格率']
fig2, axes2 = plt.subplots(nrows=1, ncols=2, figsize=(10, 5))
for i, column in enumerate(qualification_stats_columns):
    axes2[i].bar(stats_per_line.index, stats_per_line[column], color='pink')
    axes2[i].set_title(f'{column}')
    axes2[i].set_xlabel('生产线编号')
    axes2[i].set_ylabel('统计量')
    axes2[i].set_ylim(bottom=0.95, top=1.0)
plt.tight_layout()
# 保存路径改为目标路径
qualification_stats_filename = f"{TARGET_SAVE_PATH}/各生产线合格率统计对比图.png"
plt.savefig(qualification_stats_filename, dpi=100)
print(f'合格率统计对比图保存为：{qualification_stats_filename}')
plt.close()


print(f"\n=== 所有图表已统一保存到目标路径：{TARGET_SAVE_PATH} ===")